#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define BEEPDEV_CNT 1
#define BEEPDEV_NAME "platbeep"
#define OFF	0
#define ON	1

static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO019;
static void __iomem *SW_PAD_GPIO1_IO019;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;


struct chrdev_beep {
	dev_t devid;		//设备号
	struct cdev cdev;	//字符设备
	struct class *class;	//类
	struct device *device; //主设备号
	int major;	
};

struct chrdev_beep devbeep;



int beep_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &devbeep;
	return 0;
}

void beep_switch(u8 sta)
{
	u32 val = 0;
	if(sta == ON){
		val = readl(GPIO1_DR);
		val|= (1 << 19);		
		writel(val, GPIO1_DR);
	}else if(sta == OFF){
		val = readl(GPIO1_DR);
		val &= ~(1 << 19);
		writel(val, GPIO1_DR);
	}	
}

static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char gpiostat;
	retvalue = copy_from_user(databuf, buf, cnt);	//从内核读取状态
	if(retvalue < 0) {
		return -EFAULT;
	}

	gpiostat = databuf[0];		/* 获取状态值 */
	if(gpiostat == ON) {
		beep_switch(ON);		/* 打开LED灯 */
	}else if(gpiostat == OFF) {
		beep_switch(OFF);	/* 关闭LED灯 */
	}
	return 0;
}


static struct file_operations beep_ops = {
	.owner = THIS_MODULE,
	.open = beep_open,
	.write = beep_write,
};

//当设备驱动匹配成功时 执行probe函数 
int beep_probe(struct platform_device *pdev){
    unsigned int val;
	// int ressize[5];
	int i;
	struct resource *GPIO_resource[5];


    printk("Beep_probe_function run!\n");

    for(i=0;i<5;i++)
    {
        GPIO_resource[i] = platform_get_resource(pdev,IORESOURCE_MEM,i);
        if(GPIO_resource[i] == NULL){
            dev_err(&pdev->dev, "No MEM resource for always on\n");
			return -ENXIO;
        }
    //    ressize[i] = resource_size(GPIO_resource[i]);
    }
    

    //初始化蜂鸣器
    /* 2、初始化LED */
	/* 寄存器地址映射 */
 	IMX6U_CCM_CCGR1 = ioremap(GPIO_resource[0]->start, 4);
	SW_MUX_GPIO1_IO019 = ioremap(GPIO_resource[1]->start, 4);
  	SW_PAD_GPIO1_IO019 = ioremap(GPIO_resource[2]->start, 4);
	GPIO1_DR = ioremap(GPIO_resource[3]->start, 4);
	GPIO1_GDIR = ioremap(GPIO_resource[4]->start, 4);
	
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26);				/* 清除以前的设置 */
	val |= (3 << 26);				/* 设置新值 */
	writel(val, IMX6U_CCM_CCGR1);

	/* 设置GPIO1_IO03复用功能，将其复用为GPIO1_IO019 */
	writel(5, SW_MUX_GPIO1_IO019);
	writel(0x10B0, SW_PAD_GPIO1_IO019);

	/* 设置GPIO1_IO019为输出功能 */
	val = readl(GPIO1_GDIR);
	val &= ~(1 << 19);			/* 清除以前的设置 */
	val |= (1 << 19);			/* 设置为输出 */
	writel(val, GPIO1_GDIR);

	/* 默认关闭Beep */
	val = readl(GPIO1_DR);
	val &= ~(1 << 19);	
	writel(val, GPIO1_DR);
	
	/* 注册字符设备驱动 */
	/*1、创建设备号 */
	if(devbeep.major){
		devbeep.devid = MKDEV(devbeep.major,0);
		register_chrdev_region(devbeep.devid,BEEPDEV_CNT,BEEPDEV_NAME);
	}
	else {
		alloc_chrdev_region(&devbeep.devid,0,BEEPDEV_CNT,BEEPDEV_NAME);
		devbeep.major = MAJOR(devbeep.devid);
	}

	// /* 2、初始化cdev */
	devbeep.cdev.owner = THIS_MODULE;
	cdev_init(&devbeep.cdev,&beep_ops);

	//添加cdev
	cdev_add(&devbeep.cdev,devbeep.devid,BEEPDEV_CNT);

	//创建类
	devbeep.class = class_create(THIS_MODULE,BEEPDEV_NAME);
	if(IS_ERR(devbeep.class)){
		return PTR_ERR(devbeep.class);
	}

	/* 5、创建设备 */
	devbeep.device = device_create(devbeep.class,NULL,devbeep.devid,NULL,BEEPDEV_NAME);
	if(IS_ERR(devbeep.device)){
		return PTR_ERR(devbeep.device);
	}

    return 0;
}





int beep_remove(struct platform_device *pdev){
	//释放io
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO019);
	iounmap(SW_PAD_GPIO1_IO019);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);

	cdev_del(&devbeep.cdev);
	unregister_chrdev_region(devbeep.devid,BEEPDEV_CNT);
	device_destroy(devbeep.class,devbeep.devid);
	class_destroy(devbeep.class);
	
    printk("beep_driver_remove");
    return 0;
}

const struct platform_device_id beep_idtable = {
    .name = "imx6ull-beep",
};

struct platform_driver beep_driver = {
    .driver= {
        .owner = THIS_MODULE,
        .name = "imx6ull-beep",
    },
    .probe = beep_probe,
    .remove = beep_remove,
    .id_table = &beep_idtable,
};

static int beep_driver_init(void){
    int ret = 0;
    ret = platform_driver_register(&beep_driver);
    if(ret < 0 )
    {
        printk("platform_driver_register error \n");

    }
    //初始化匹配成功就ok
    printk("platform_driver_register ok \n");
	
	return 0;
}

static void beep_driver_exit(void){
    platform_driver_unregister(&beep_driver);
    printk("Beep Dirver Uninstall!\n");
}

module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");